package com.codemeta.bokit.comm.kit;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import org.springframework.util.CollectionUtils;

/**
 * JDK8.stream流操作工具类
 *
 * @author 美萌:hechen
 * @date 2020/1/16 9:41
 */
public class StreamKit {

    /**
     * 带索引迭代
     *
     * @param elements 元素
     * @param action   消费函数
     * @param <E>      泛型
     */
    public static <E> void forEach(
        Iterable<? extends E> elements, BiConsumer<Integer, ? super E> action) {
        Objects.requireNonNull(elements);
        Objects.requireNonNull(action);
        int index = 0;
        for (E element : elements) {
            action.accept(index++, element);
        }
    }

    /**
     * 映射
     *
     * @param data 参数集合
     * @param fun  函数
     * @param <T>  参数泛型
     * @param <R>  返回泛型
     * @return 返回R或空集合
     */
    public static <T, R> List<R> map(List<T> data, Function<T, R> fun) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().map(fun).filter(Objects::nonNull).collect(Collectors.toList());
    }

    public static <T, R> List<R> mapDistinct(List<T> data, Function<T, R> fun) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().map(fun).filter(Objects::nonNull).distinct()
                .collect(Collectors.toList());
    }

    /**
     * 过滤
     *
     * @param data 参数集合
     * @param pre  校验函数
     * @param <T>  泛型
     * @return data为空返回data
     */
    public static <T> List<T> filter(List<T> data, Predicate<T> pre) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().filter(pre).collect(Collectors.toList());
    }

    /**
     * 过来并查找任意一个值
     *
     * @param data 参数集合
     * @param pre  函数
     * @param <T>  泛型
     * @return 值
     */
    public static <T> T any(List<T> data, Predicate<T> pre) {
        return CollUtil.isEmpty(data) ? null : data.stream().filter(pre).findAny().orElse(null);
    }

    public static <T> T first(Collection<T> data, Predicate<T> pre) {
        return CollUtil.isEmpty(data) ? null : data.stream().filter(pre).findFirst().orElse(null);
    }

    /**
     * 排序
     *
     * @param data       参数集合
     * @param comparator 排序函数
     * @param <T>        泛型
     * @return data为空返回data
     */
    public static <T> List<T> sorted(List<T> data, Comparator<T> comparator) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().sorted(Comparator.nullsLast(comparator)).collect(Collectors.toList());
    }

    /**
     * 去重
     *
     * @param data 参数集合
     * @param <T>  泛型
     * @return data为空返回data
     */
    public static <T> List<T> distinct(List<T> data) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().distinct().filter(Objects::nonNull).collect(Collectors.toList());
    }

    public static <T> Collection<T> distinctMap(Set<T> data, Function<? super T, Object> key) {
        return CollUtil.isEmpty(data) ? new ArrayList<>() : data.stream()
            .collect(Collectors.toMap(key, Function.identity(), (ov, nv) -> nv)).values();
    }

    /**
     * 自定义去重
     *
     * @param keyExtractor .
     * @param <T>          .
     * @return .
     */
    public static <T> Predicate<T> distinctKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>(16);
        return t -> !Objects.isNull(t)
            && seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    public static <T> List<T> distinct(List<T> data, Function<? super T, Object> key) {
        return CollUtil.isEmpty(data) ? new ArrayList<>()
            : data.stream().filter(distinctKey(key)).collect(Collectors.toList());
    }

    /**
     * 判断是否包含匹配元素
     *
     * @param data 参数集合
     * @param pre  校验函数
     * @param <T>  泛型
     * @return data为空返回false
     */
    public static <T> boolean anyMatch(List<T> data, Predicate<T> pre) {
        return !CollUtil.isEmpty(data) && data.parallelStream().anyMatch(pre);
    }

    public static <T> boolean allMatch(List<T> data, Predicate<T> pre) {
        return !CollUtil.isEmpty(data) && data.parallelStream().allMatch(pre);
    }

    public static <T> boolean noneMatch(List<T> data, Predicate<T> pre) {
        return !CollUtil.isEmpty(data) && data.parallelStream().noneMatch(pre);
    }

    /**
     * 将list进行join操作
     *
     * @param data 参数集合
     * @param join 拼接符
     * @return 返回join之后的字符串, data为空返回null
     */
    public static String join(List<String> data, String join) {
        return CollUtil.isEmpty(data) ? null
            : data.stream().collect(Collectors.joining(join == null ? "" : join));
    }

    /**
     * 获取单个过滤对象
     *
     * @param data 对象列表
     * @param pre  过滤条件
     * @param <T>  泛型
     * @return 单个对象
     */
    public static <T> T get(List<T> data, Predicate<T> pre) {
        if (CollectionUtils.isEmpty(data)) {
            return null;
        }
        List<T> list = data.stream().filter(pre).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        if (list.size() > 1) {
            throw new IllegalArgumentException("数据重复异常");
        }
        return list.get(0);
    }

    /**
     * 映射转hashSet
     *
     * @param data 参数集合
     * @param fun  函数
     * @param <T>  参数泛型
     * @param <R>  返回泛型
     * @return data为空抛出异常IllegalArgumentException
     */
    public static <T, R> Set<R> toSet(List<T> data, Function<T, R> fun) {
        return CollUtil.isEmpty(data) ? Collections.emptySet()
            : data.stream().map(fun).collect(Collectors.toSet());
    }

    /**
     * 分组列表
     *
     * @param data list
     * @param fun  .
     * @param <T>  .
     * @param <R>  .
     * @return .
     */
    public static <T, R> Map<R, List<T>> group(List<T> data, Function<T, R> fun) {
        return CollUtil.isEmpty(data) ? Collections.emptyMap()
            : data.stream().collect(Collectors.groupingBy(fun));
    }

    /**
     * 有序分组
     *
     * @param data 数据
     * @param fun  分组函数
     * @param <T>  。
     * @param <R>  。
     * @return 。
     */
    public static <T, R> Map<R, List<T>> groupOrder(List<T> data, Function<T, R> fun) {
        return CollUtil.isEmpty(data) ? Collections.emptyMap() : data.stream()
            .collect(Collectors.groupingBy(fun, LinkedHashMap::new, Collectors.toList()));
    }

    /**
     * 统计
     *
     * @param data .
     * @param fun  .
     * @param opt  .
     * @param <T>  .
     * @param <R>  .
     * @return .
     */
    public static <T, R> R mapReduce(List<T> data, Function<T, R> fun, BinaryOperator<R> opt) {
        return CollUtil.isEmpty(data) ? null : data.stream().map(fun).reduce(opt).orElse(null);
    }

    public static <T> Integer mapReduceInt(List<T> data, Function<T, Integer> fun) {
        return CollUtil.isEmpty(data) ? null
            : data.stream().map(fun).reduce(Integer::sum).orElse(0);
    }

    public static <T> BigDecimal mapReduceDecimal(List<T> data, Function<T, BigDecimal> fun) {
        return CollUtil.isEmpty(data) ? null
            : data.stream().map(fun).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
    }

    /**
     * 统计
     *
     * @param data 数据
     * @param opt  函数
     * @param <T>  泛型
     * @return 值
     */
    public static <T> T mapReduce(List<T> data, BinaryOperator<T> opt) {
        return CollUtil.isEmpty(data) ? null : data.stream().reduce(opt).orElse(null);
    }

    /**
     * 平均值
     *
     * @param data 数据
     * @param fun  函数
     * @param <T>  泛型
     * @return 值
     */
    public static <T> Double avg(List<T> data, ToDoubleFunction<? super T> fun) {
        return CollUtil.isEmpty(data) ? null : data.stream().mapToDouble(fun).average().orElse(0);
    }

    /**
     * 取集合指定KV数据并返回kv.map数据
     *
     * @param data .
     * @param fk   .
     * @param fv   .
     * @param <T>  .
     * @param <K>  .
     * @param <V>  .
     * @return .
     */
    public static <T, K, V> Map<K, V> toMap(List<T> data, Function<T, K> fk, Function<T, V> fv) {
        return CollUtil.isEmpty(data) ? MapUtil.newHashMap() : data.stream().collect(Collectors.toMap(fk, fv));
    }

    /**
     * 取集合指定KV数据并返回有序的kv.map数据 键相同时会覆盖
     *
     * @param data .
     * @param fk   .
     * @param fv   .
     * @param <T>  .
     * @param <K>  .
     * @param <V>  .
     * @return .
     */
    public static <T, K, V> LinkedHashMap<K, V> toLinkedMap(List<T> data, Function<T, K> fk,
        Function<T, V> fv) {
        return data.stream().collect(Collectors.toMap(fk, fv, (v1, v2) -> v2, LinkedHashMap::new));
    }

    /**
     * 过滤并统计数据量
     *
     * @param data .
     * @param pre  .
     * @param <T>  .
     * @return .
     */
    public static <T> long count(List<T> data, Predicate<T> pre) {
        return CollUtil.isEmpty(data) ? 0L : data.stream().filter(pre).count();
    }
}